package server.buscador;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeMap;


public class AStarSearch {

	
	private TreeMap<Integer, ANode>  frontera;
	private HashMap<Integer, ANode>  visitados; 
	private SpaceNode meta;
	
	public AStarSearch(){
		 frontera = new TreeMap<Integer, ANode>();
		 visitados = new HashMap<Integer, ANode>(50);
	}
	
	public ANode search(SpaceNode init, SpaceNode meta){
		frontera.clear();
		visitados.clear();
		this.meta = meta;
			
		ANode init_ = new ANode();
		ANode curr;
		List<ANode> vecinos;

		init_.nodo = init;
		init_.camino = new ArrayList<SpaceNode>();
		init_.costo = 0;
		init_.h = 0;
		frontera.put(init_.nodo.getID(),init_);

		while(true){
			curr = selectNode(); //
			if(curr == null) return null;
			System.out.println("Recorriendo: "+ curr.nodo.getName());
			if(isGoal(curr))
				return curr; //lo encontre!

			vecinos = getNeighbours(curr);
			addNeighbours(vecinos);
			visitados.put(curr.nodo.getID(), curr);
		}
	}

	public void addNeighbours(List<ANode> vecinos) {
		for (ANode aNode : vecinos) {
			ANode aux = visitados.get(aNode.nodo.getID());
			if(aux != null){ //si estaba en visitados, no va a estar en forntera
				if(aNode.costo<aux.costo){
					visitados.remove(aNode.nodo.getID());
					frontera.put(aNode.nodo.getID(), aNode);
				} //sino no lo agrego porque tiene mayor costo
				continue; 
			} //no estaba visitado
			aux = frontera.get(aNode.nodo.getID());
			if(aux == null || aNode.costo<aux.costo){ //si no estaba o el costo era mayor
				frontera.put(aNode.nodo.getID(), aNode); //lo reemplaza el metodo
			} //else estaba y el costo era menor
		}
	}

	public List<ANode> getNeighbours(ANode curr) {
		List<ANode> vecinos = new LinkedList<ANode>();
		for(NodeEdge p: curr.nodo.getPortals()){
			if(p.target != null) //esta linkeado
			vecinos.add(crearNodoToTarget(curr, p));
		};
		return vecinos;
	}

	public ANode crearNodoToTarget(ANode curr, NodeEdge p) {
		SpaceNode vecino = (SpaceNode) p.getTarget();
		ANode nuevo = new ANode();
		nuevo.nodo = vecino;
		nuevo.camino = copyPath(curr.camino);
		nuevo.camino.add(curr.nodo);
		nuevo.h = (int) nuevo.nodo.getCentro().distance(meta.getCentro());
		nuevo.costo = (int) (curr.costo + curr.nodo.getCentro().distance(nuevo.nodo.getCentro())); 
		return nuevo;
	}


	public List<SpaceNode> copyPath(List<SpaceNode> camino) {
		List<SpaceNode> nueva = new ArrayList<SpaceNode>(camino.size()+1);
		for(SpaceNode sn : camino){
			nueva.add(sn);
		}
		return nueva;
	}

	public boolean isGoal(ANode curr) {
		return curr.nodo == meta;
	}

	public ANode selectNode() {
		int hMin= Integer.MAX_VALUE;
		int idMin = -1;
		for (ANode n : frontera.values()) {
			//System.out.println(n.nodo.getName()+ n.getEuristica());
			if(n.getEuristica()< hMin){
				hMin = (int)n.getEuristica();
				idMin = n.nodo.getID();
			}
		}
		return frontera.remove(idMin);
	}

}
